package org.jaaksi.customviewdemo.view

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.core.graphics.toColorInt
import androidx.core.view.doOnPreDraw
import org.jaaksi.customviewdemo.chart.DataPoint
import org.jaaksi.customviewdemo.chart.Scale
import org.jaaksi.customviewdemo.chart.toSmoothLinePath3
import kotlin.math.max

/**
 * 左3个点，右5个点，中间波峰波谷4个点。
 * 中间波峰波谷的高度随着测量到的bpm值变化而变化，例如总高度*0.75+bpm/220（最大心率值）*0.25
 * 左边和右边的点的值应该是随机值
 * 图表min和max scale固定的，如-100~100
 *
 * 1.与峰谷相连接的小波段之间贝塞尔是中断的
 * todo
 * 2.峰谷的宽度和其他片段的宽是不一样的。4个点相当于3个点宽度
 * 3.右边是一点点出来的，使用画布裁减
 * 4.移动的速度好像还不一样。如果要控制速度，需要给view加个动画结束的回调，动画结束后，send新的
 *
 * 一个波段11个点，一屏最多展示3个波段，
 *
 */
class WaveView @JvmOverloads constructor(
    context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    //画笔
    private var mPaint: Paint = Paint()

    //折现的颜色
    private var mLineColor = "#F7305A".toColorInt()

    //心电图折现
    private val mPath: Path = Path()
    private val data = mutableListOf<DataPoint>()

    // 两点之间的距离
    private var spaceWith = 0f

    // 上升波宽度
    private var riseWith = 0f

    // 下降波宽度
    private var declineWith = 0f

    private var scale: Scale = Scale(-110f, 110f)
    private var frame = RectF()
    private var animFrame = RectF()
    private var clipFrame = RectF()

    private var anim: ValueAnimator? = null
    private var clipAnim: ValueAnimator? = null

    // 一屏最多多少个波段
    var maxWaveBand = 3
    private val peakRatio = 2 * 0.7f

    companion object {
        const val MIN_POINT_SIZE = 13
        const val DURATION = 1000L
    }

    // 由于曲线是动态的，所以不能直接设置shader，这里使用Xfermode，只绘制交集
    private var xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

    private val linearGradient by lazy(LazyThreadSafetyMode.NONE) {
        LinearGradient(
            0f,
            0f,
            width.toFloat(),
            height.toFloat(),
            intArrayOf(Color.BLUE, "#F7305A".toColorInt()),
            null,
            Shader.TileMode.MIRROR
        )
    }

    init {
        //禁用硬件加速，某些API不支持硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        // todo 设置了但是交接处还不圆润呢
        mPaint.strokeCap = Paint.Cap.ROUND
        mPaint.isAntiAlias = true
        mPaint.style = Paint.Style.STROKE
        mPaint.color = mLineColor
        mPaint.strokeWidth = 8f

    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 一个波段中，小波端占用9个，峰谷占2。这里要+2因为一屏三个，没两个之间会多处一个
        spaceWith = w / (maxWaveBand * (9 + peakRatio) + 2)
        // 峰谷占据2个space，下降段占1/5,两个上升段各占2/5
        // 如果要占据的更短，这里totalWith * 0.x
        val totalWith = spaceWith * peakRatio
        riseWith = totalWith * 2 / 5
        declineWith = totalWith - riseWith * 2
        frame.set(0f, 0f, w.toFloat(), h.toFloat())
        animFrame.set(frame)
        clipFrame.set(frame)
    }

    // 要在setData之前调用
    fun setScale(scale: Scale) {
        this.scale = scale
    }

    /**
     * @param data 至少3条数据
     * @param withAnim 是否进行动画
     */
    fun setData(data: List<Float>, withAnim: Boolean = false) {
        this.data.clear()
        if (data.size >= MIN_POINT_SIZE) {
            this.data.addAll(data.map { DataPoint(it) })
            doOnPreDraw {
                initDataPoints()
                // 开始动画
                if (withAnim) {
                    startAnim()
                } else {
                    anim?.end()
                    anim = null
                    clipAnim?.end()
                    clipAnim = null
                }
            }
        }
        postInvalidate()
    }

    private fun startAnim() {
        anim?.end()
        anim = null
        clipAnim?.end()
        clipAnim = null
//        val lastX = data.last().screenPositionX
        val startIndex = max(data.lastIndex - MIN_POINT_SIZE, 0)
        anim = ValueAnimator.ofFloat(
            data[startIndex].screenPositionX,
            data.last().screenPositionX
        ).also {
            it.duration = DURATION
        }
//        if (lastX > frame.right) {
//            // 超过一屏幕，移动画布
//            clipAnim = ValueAnimator.ofFloat(
//                data.last().screenPositionX - data[data.lastIndex - MIN_POINT_SIZE - 2].screenPositionX, // 限制数组个数，只多处一个group后，这里可以写frame.right
//                0f
//            ).also {
//                it.duration = DURATION / 2
//            }
//        }
//        clipAnim?.apply {
//            interpolator = LinearInterpolator()
//            addUpdateListener {
//                clipFrame.right = frame.right - (it.animatedValue as Float) / 2f
//                println(">> clipFrame.right = ${clipFrame.right}")
//            }
//            start()
//        }
        anim?.apply {
            interpolator = LinearInterpolator()
            addUpdateListener {
                animFrame.right = it.animatedValue as Float
//                clipFrame.right = frame.right - (data.last().screenPositionX - animFrame.right) / 2f
                postInvalidate()
            }
            start()
        }


    }


    private fun initDataPoints() {
        if (data.size < MIN_POINT_SIZE) return

        val scaleSize = scale.size
        var offset = 0f
        // // 如果第一条数据为波谷则奇数为波谷，偶数为波峰，反之
        data.forEachIndexed { index, dataPoint ->
            // 前一个的坐标+当前点距下个点距离
            if (index == 0) {
                dataPoint.screenPositionX = frame.left + index * spaceWith
            } else {
                offset = when {
                    dataPoint.value > 30f -> riseWith
                    dataPoint.value < -30f -> declineWith
                    dataPoint.value == 0f && data[index - 1].value < -30f -> riseWith
                    else -> spaceWith
                }
                dataPoint.screenPositionX = data[index - 1].screenPositionX + offset
            }
            dataPoint.screenPositionY =
                frame.bottom -
                        (height * (dataPoint.value - scale.min) / scaleSize)
        }
    }

    override fun onDraw(canvas: Canvas) {
        if (data.size < MIN_POINT_SIZE) return
        data.toSmoothLinePath3(mPath)
        mPaint.shader = null
        mPaint.xfermode = null
        mPaint.style = Paint.Style.STROKE
        when {
            // 不足一屏时，根据动画裁减画布
            animFrame.right <= frame.right -> {
                mPaint.strokeWidth = 8f
//                mPaint.color = "#F7305A".toColorInt()
                canvas.save()
                canvas.clipRect(animFrame)
                canvas.drawPath(mPath, mPaint)
//                drawSupportLine(canvas)
                canvas.restore()
            }
            else -> { // 超过一屏后，移动画布
                val clipId = canvas.save()
//                canvas.clipRect(clipFrame)
//                val saveId = canvas.save()
                canvas.translate(-(animFrame.right - frame.right), 0f)
                mPaint.strokeWidth = 8f
//                mPaint.color = "#F7305A".toColorInt()
                canvas.drawPath(mPath, mPaint)
//                drawSupportLine(canvas)
//                canvas.restoreToCount(saveId)
                canvas.restoreToCount(clipId)
            }
        }
        mPaint.shader = linearGradient
        mPaint.style = Paint.Style.FILL
        mPaint.xfermode = xfermode
        canvas.drawRect(frame, mPaint)

    }

    private fun drawSupportLine(canvas: Canvas) {
        mPaint.strokeWidth = 1f
        mPaint.color = Color.BLUE
        data.forEachIndexed { index, dataPoint ->
            canvas.drawLine(
                dataPoint.screenPositionX,
                0f,
                dataPoint.screenPositionX,
                if (index == data.lastIndex - 1) frame.bottom else dataPoint.screenPositionY,
                mPaint
            )
        }
    }

}